#include "oled_software_i2c.h"
#include "oledfont.h"
#include "Driver/CLOCK/clock.h"
#include <string.h>

// 屏幕缓冲区
static uint8_t OLED_GRAM[128][8];

// 内部使用的I2C操作函数
static void I2C_Start(void);
static void I2C_Stop(void);
static void I2C_WaitAck(void);
static void Send_Byte(uint8_t dat);
static void OLED_WR_Byte(uint8_t dat,uint8_t mode);
static void OLED_Set_Pos(uint8_t x, uint8_t y);
static uint32_t oled_pow(uint8_t m,uint8_t n);

//OLED的显存
//存放格式如下.
//[0]0 1 2 3 ... 127	
//[1]0 1 2 3 ... 127	
//[2]0 1 2 3 ... 127	
//[3]0 1 2 3 ... 127	
//[4]0 1 2 3 ... 127	
//[5]0 1 2 3 ... 127	
//[6]0 1 2 3 ... 127	
//[7]0 1 2 3 ... 127

void delay_ms(uint32_t ms)
{
    mspm0_delay_ms(ms);
}

//反显函数
void OLED_ColorTurn(uint8_t i)
{
    if(i==0)
    {
        OLED_WR_Byte(0xA6,OLED_CMD);//正常显示
    }
    if(i==1)
    {
        OLED_WR_Byte(0xA7,OLED_CMD);//反色显示
    }
}

//屏幕旋转180度
void OLED_DisplayTurn(uint8_t i)
{
    if(i==0)
    {
        OLED_WR_Byte(0xC8,OLED_CMD);//正常显示
        OLED_WR_Byte(0xA1,OLED_CMD);
    }
    if(i==1)
    {
        OLED_WR_Byte(0xC0,OLED_CMD);//反转显示
        OLED_WR_Byte(0xA0,OLED_CMD);
    }
}

//起始信号
static void I2C_Start(void)
{
    OLED_SDA_Set();
    OLED_SCL_Set();
    mspm0_delay_us(2);  // 使用微秒级延时
    OLED_SDA_Clr();
    mspm0_delay_us(2);  // 使用微秒级延时
    OLED_SCL_Clr();
    mspm0_delay_us(2);  // 使用微秒级延时
}

//结束信号
static void I2C_Stop(void)
{
    OLED_SDA_Clr();
    mspm0_delay_us(2);  // 使用微秒级延时
    OLED_SCL_Set();
    mspm0_delay_us(2);  // 使用微秒级延时
    OLED_SDA_Set();
    mspm0_delay_us(2);  // 使用微秒级延时
}

//等待信号响应
static void I2C_WaitAck(void)
{
    uint8_t ack;
    
    OLED_SDA_IN();     // SDA设置为输入  
    OLED_SDA_Set();    // 释放SDA线
    mspm0_delay_us(2);
    OLED_SCL_Set();    // SCL=1，此时从设备会拉低SDA产生ACK信号
    mspm0_delay_us(2);
    
    ack = OLED_SDA_Read(); // 读取SDA状态，应该为0（有应答）
    
    OLED_SCL_Clr();    // SCL=0
    mspm0_delay_us(2);
    OLED_SDA_OUT();    // SDA设置为输出
    
    // 即使没有收到ACK也继续执行，避免通信中断
    // 但在实际应用中，可以根据ack返回值决定是否重试
}

//写入一个字节
static void Send_Byte(uint8_t dat)
{
    uint8_t i;
    for(i=0;i<8;i++)
    {
        OLED_SCL_Clr();//将时钟信号设置为低电平
        mspm0_delay_us(2);  // 使用微秒级延时
        if(dat&0x80)//将dat的8位从最高位依次写入
        {
            OLED_SDA_Set();
        }
        else
        {
            OLED_SDA_Clr();
        }
        mspm0_delay_us(2);  // 使用微秒级延时
        OLED_SCL_Set();
        mspm0_delay_us(2);  // 使用微秒级延时
        OLED_SCL_Clr();
        dat<<=1;
    }
}

//发送一个字节
//向SSD1306写入一个字节。
//mode:数据/命令标志 0,表示命令;1,表示数据;
static void OLED_WR_Byte(uint8_t dat,uint8_t mode)
{
    I2C_Start();
    Send_Byte(0x78);
    I2C_WaitAck();
    if(mode){Send_Byte(0x40);}
    else{Send_Byte(0x00);}
    I2C_WaitAck();
    Send_Byte(dat);
    I2C_WaitAck();
    I2C_Stop();
}

//坐标设置
static void OLED_Set_Pos(uint8_t x, uint8_t y) 
{ 
    OLED_WR_Byte(0xb0+y,OLED_CMD);
    OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
    OLED_WR_Byte((x&0x0f),OLED_CMD);
}

//开启OLED显示    
void OLED_Display_On(void)
{
    OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
    OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ON
    OLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON
}

//关闭OLED显示     
void OLED_Display_Off(void)
{
    OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
    OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFF
    OLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF
}

//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!	  
void OLED_Clear(void)  
{  
    memset(OLED_GRAM, 0x00, sizeof(OLED_GRAM));
}

//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63				 
//sizey:选择字体 6x8  8x16
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t sizey)
{      	
    uint8_t c = chr - ' ';
    uint16_t i = 0;
    uint8_t sizex = sizey / 2;

    if (x > 127 || y > 7) return;

    if (sizey == 16)
    {
        // 写入8x16字体上半部分
        for (i = 0; i < sizex; i++)
        {
            if (x + i < 128)
            {
                OLED_GRAM[x + i][y] = asc2_1608[c][i];
            }
        }
        // 写入8x16字体下半部分
        for (i = 0; i < sizex; i++)
        {
            if (x + i < 128 && y + 1 < 8)
            {
                OLED_GRAM[x + i][y + 1] = asc2_1608[c][i + sizex];
            }
        }
    }
    else if (sizey == 8)
    {
        sizex = 6; // 6x8 字体
        // 写入6x8字体
        for (i = 0; i < sizex; i++)
        {
            if (x + i < 128)
            {
                OLED_GRAM[x + i][y] = asc2_0806[c][i];
            }
        }
    }
}

//m^n函数
static uint32_t oled_pow(uint8_t m,uint8_t n)
{
    uint32_t result=1;	 
    while(n--)result*=m;    
    return result;
}

//显示数字
//x,y :起点坐标
//num:要显示的数字
//len :数字的位数
//sizey:字体大小		  
void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t sizey)
{         	
    uint8_t t,temp,m=0;
    uint8_t enshow=0;
    uint8_t char_width = sizey/2;

    if(sizey==8)
    {
        m=2; // 为6x8字体额外增加间距
        char_width = 6;
    }

    for(t=0;t<len;t++)
    {
        temp=(num/oled_pow(10,len-t-1))%10;
        if(enshow==0&&t<(len-1))
        {
            if(temp==0)
            {
                OLED_ShowChar(x+t*char_width,y,' ',sizey);
                continue;
            }else enshow=1;
        }
        OLED_ShowChar(x+t*char_width,y,temp+'0',sizey);
    }
}

//显示一个字符号串
void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t sizey)
{
    uint8_t j=0;
    while (chr[j]!='\0')
    {		
        OLED_ShowChar(x,y,chr[j++],sizey);
        if(sizey==8)
        {
            x+=6;
        }
        else
        {
            x+=sizey/2;
        }
    }
}

//显示汉字
void OLED_ShowChinese(uint8_t x,uint8_t y,uint8_t no,uint8_t sizey)
{
    uint16_t i;
    uint8_t sizex = sizey;
    if (sizey == 16)
    {
        for(i=0; i<sizex; i++)
        {
            if (x+i < 128 && y < 7)
            {
                OLED_GRAM[x+i][y] = Hzk[no][i];
                OLED_GRAM[x+i][y+1] = Hzk[no][i+16];
            }
        }
    }
    // 其他字号暂不支持
}

//显示图片
//x,y显示坐标
//sizex,sizey,图片长宽
//BMP：要显示的图片
void OLED_DrawBMP(uint8_t x,uint8_t y,uint8_t sizex, uint8_t sizey,uint8_t BMP[])
{ 	
    uint16_t j=0;
    uint8_t i,m;
    uint8_t page_height = sizey/8 + ((sizey%8)?1:0);
    for(i=0; i<page_height; i++)
    {
        for(m=0; m<sizex; m++)
        {      
            if(x+m < 128 && y+i < 8)
            {
                OLED_GRAM[x+m][y+i] = BMP[j++];
            }
        }
    }
}

// 将屏幕缓冲区的数据刷新到OLED屏幕
void OLED_Refresh(void)
{
    uint8_t i, n;
    for (i = 0; i < 8; i++)
    {
        I2C_Start();
        Send_Byte(0x78);     // Slave address
        I2C_WaitAck();
        Send_Byte(0x00);     // Write command
        I2C_WaitAck();
        Send_Byte(0xb0 + i); // Set page address
        I2C_WaitAck();
        Send_Byte(0x00);     // Set low column address
        I2C_WaitAck();
        Send_Byte(0x10);     // Set high column address
        I2C_WaitAck();
        I2C_Stop();

        I2C_Start();
        Send_Byte(0x78); // Slave address
        I2C_WaitAck();
        Send_Byte(0x40); // Write data mode
        I2C_WaitAck();
        for (n = 0; n < 128; n++)
        {
            Send_Byte(OLED_GRAM[n][i]);
            I2C_WaitAck();
        }
        I2C_Stop();
    }
}

//初始化SSD1306					    
void OLED_Init(void)
{
    mspm0_delay_ms(200);
    
    OLED_SDA_OUT();
    
    for(uint8_t i = 0; i < 3; i++) {
        I2C_Start();
        mspm0_delay_us(10);
        I2C_Stop();
        mspm0_delay_us(10);
    }

    OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
    OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
    OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
    OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
    OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
    OLED_WR_Byte(0xCF,OLED_CMD); // Set SEG Output Current Brightness
    OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
    OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
    OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
    OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
    OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
    OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
    OLED_WR_Byte(0x00,OLED_CMD);//-not offset
    OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
    OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
    OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
    OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
    OLED_WR_Byte(0x12,OLED_CMD);
    OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
    OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
    OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
    OLED_WR_Byte(0x02,OLED_CMD);//
    OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
    OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
    OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On
    OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On
    
    mspm0_delay_ms(20);
    
    OLED_Clear();
    
    OLED_Refresh();
    
    OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/
    mspm0_delay_ms(50);
}  
